﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Linq.Expressions;
using System.Net.Http;
using System.Threading;
using Common.Logging;
using In.Api.Dto.HisWs;
using Newtonsoft.Json;
using Out.Service.Helpers;
using Out.Service.HttpClients;
using OutDbContext;
using Rmq.Center;
using Senparc.Weixin.MP.AdvancedAPIs;

namespace Out.Service.Services
{
    public class BizApptOrder : IDisposable
    {
        // 0未锁号 
        // 1锁号中 2锁号失败 3已锁号 
        // 4取消锁号中 5取消锁号成功 6取消锁号失败 
        // 7预约中 8预约成功 9预约失败 
        // 10取消预约中 11取消预约成功 12取消预约失败 
        // 13支付中 14支付失败 15支付成功 
        // 16取号中 17取号失败 18退款中 19退款成功 20退款失败 21取号成功 
        // 22微信退号中 23微信退号失败 24微信退号成功 
        // 25微信退号退款中 26微信退号退款失败 27微信退号退款成功 
        // 28窗口退号成功
        public enum OrderStatus
        {
            未锁号 = 0,
            锁号中 = 1,
            锁号失败 = 2,
            锁号成功 = 3,
            取消锁号中 = 4,
            取消锁号成功 = 5,
            取消锁号失败 = 6,
            预约中 = 7,
            预约成功 = 8,
            预约失败 = 9,
            取消预约中 = 10,
            取消预约成功 = 11,
            取消预约失败 = 12,
            支付中 = 13,
            支付失败 = 14,
            支付成功 = 15,
            取号中 = 16,
            取号失败 = 17,
            退款中 = 18,
            退款成功 = 19,
            退款失败 = 20,
            取号成功 = 21,
            微信退号中 = 22,
            微信退号失败 = 23,
            微信退号成功 = 24,
            微信退号退款中 = 25,
            微信退号退款失败 = 26,
            微信退号退款成功 = 27,
            窗口退号成功 = 28
        }

        public enum OrderType
        {
            预约 = 0,
            其它平台预约 = 1,
            直接挂号 = 2
        }

        /// <summary>
        /// 支付状态:0-未付款,1-已付款,2-退款中,3-已退款,4-退款失败
        /// </summary>
        public enum PayState
        {
            未付款 = 0,
            已付款 = 1,
            退款中 = 2,
            已退款 = 3,
            退款失败 = 4
        }

        private static readonly ILog Logger = LogManager.GetLogger(typeof(BizApptOrder));

        private static readonly object OrderNoLocker = new object();

        private readonly GdsfybjyWeiXinEntities _ctx;
        private int _orderId;
        private readonly string _openId;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="openId">用户openid</param>
        public BizApptOrder(string openId = "")
        {
            _ctx = new GdsfybjyWeiXinEntities();
            _openId = openId;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="orderId">订单Id</param>
        /// <param name="openId">用户openid</param>
        public BizApptOrder(int orderId, string openId = "")
        {
            _ctx = new GdsfybjyWeiXinEntities();
            _orderId = orderId;
            _openId = openId;
        }

        /// <summary>
        /// 获取订单数据对象
        /// </summary>
        /// <returns></returns>
        public ApptOrder Get()
        {
            var order = _ctx.ApptOrders.IsOpenId(_openId).FirstOrDefault(x => x.Id == _orderId);
            return order;
        }

        /// <summary>
        /// 获取用户的所有订单数据
        /// 只支持查询从指定日期开始10日内的订单
        /// </summary>
        /// <param name="startDt">查询开始日期</param>
        /// <returns></returns>
        public List<ApptOrder> GetAll(DateTime startDt)
        {
            startDt = startDt.Date;
            var endDt = startDt.AddDays(10);
            var orders = _ctx.ApptOrders.IsOpenId(_openId)
                .IsStatus(OrderStatus.取号成功, OrderStatus.微信退号中, OrderStatus.微信退号失败, OrderStatus.微信退号成功,
                OrderStatus.微信退号退款中, OrderStatus.微信退号退款失败, OrderStatus.微信退号退款成功)
                .Where(x => x.CreateTime >= startDt && x.CreateTime < endDt).ToList();

            return orders;
        }

        /// <summary>
        /// 生成挂号订单号
        /// </summary>
        /// <returns></returns>
        public string GenerateNewOrderNo()
        {
            var orderNo = "WX" + DateTime.Now.Date.ToString("yyyyMMdd");
            lock (OrderNoLocker)
            {
                orderNo += (DateTime.Now - DateTime.Now.Date).TotalMilliseconds.ToString("00000000");
                Thread.Sleep(1);
            }

            return orderNo;
        }

        /// <summary>
        /// 生成预约订单
        /// </summary>
        public ResResult<ApptOrder> CreateApptOrder(string deptId, string deptName,
            string doctorId, string doctorName, string regDate, string timeSlice, string startTime, string endTime, string regType,
            decimal fee, decimal treatFee, decimal otherFee, string sqno)
        {
            var order = new ApptOrder
            {
                OpenId = _openId,
                OrderType = OrderType.预约.ToInt32().ToString(),
                HospitalId = "1036",
                DeptId = deptId,
                DeptName = deptName,
                DoctorId = doctorId,
                DoctorName = doctorName,
                OrderNo = GenerateNewOrderNo(),
                RegDate = regDate,
                OrderStatus = OrderStatus.未锁号.ToInt32(),
                TimeSlice = timeSlice,
                StartTime = startTime,
                EndTime = endTime,
                RegType = regType,
                PayState = PayState.未付款.ToInt32().ToString(),
                Fee = fee,
                TreatFee = treatFee,
                OtherFee = otherFee,
                SumFee = fee + treatFee + otherFee,
                CreateTime = DateTime.Now,
                Sqno = sqno
            };

            _ctx.ApptOrders.Add(order);
            _ctx.SaveChanges();

            _orderId = order.Id;

            var locking = Locking();
            if (locking.Result == Result.未返回 || locking.Result == Result.条件不足未执行)
            {
                return new ResResult<ApptOrder>(Result.失败, order, "获取挂号信息失败，请稍后重试");
            }
            if (locking.Result == Result.失败)
            {
                return new ResResult<ApptOrder>(Result.失败, order, locking.Message);
            }

            return new ResResult<ApptOrder>(Result.成功, order);
        }

        /// <summary>
        /// 更新预约订单的就诊卡信息
        /// </summary>
        public ResResult<ApptOrder> UpdateApptOrderHospCard(int cardId)
        {
            var order = _ctx.ApptOrders.IsOpenId(_openId).IsStatus(OrderStatus.锁号成功)
                .FirstOrDefault(x => x.Id == _orderId);

            if (order == null)
            {
                return new ResResult<ApptOrder>(Result.条件不足未执行);
            }

            var card = new BizHospCard(cardId, _openId).Get();

            var sameOrder = _ctx.ApptOrders.IsStatus(OrderStatus.取号成功)
                .FirstOrDefault(x =>
                    x.Id != order.Id
                    && x.CardNo == card.CardNo
                    && x.RegDate == order.RegDate
                    && x.DoctorId == order.DoctorId
                    && x.TimeSlice == order.TimeSlice);

            if (sameOrder != null)
            {
                return new ResResult<ApptOrder>(Result.失败, order, "同个医生同个班次，只能挂号一次!");
            }
            if (order.DeptName.Contains("内科") && (!order.DeptName.Contains("儿")))
            {
                DateTime birthday;
                DateTime.TryParse(card.Birthdate, out birthday);
                int age = DateTime.Now.Date.Year - birthday.Year;
                if (birthday.AddYears(age).CompareTo(DateTime.Now.Date) == -1)
                {
                    age = age - 1;
                }
                if (age < 18)
                {
                    return new ResResult<ApptOrder>(Result.失败, order, "年龄未满18周岁，无法在内科门诊挂号!");
                }
            }

            var cardSameDayAppointMax = ConfigurationManager.AppSettings["CardSameDayAppointMax"].ToInt32();
            var cardApptCount = _ctx.ApptOrders.IsStatus(OrderStatus.取号成功)
                .Count(x => x.CardNo == card.CardNo && x.RegDate == order.RegDate);
            if (cardApptCount >= cardSameDayAppointMax)
            {
                return new ResResult<ApptOrder>(Result.失败, order, "同一天最多可以预约挂号" + cardSameDayAppointMax + "次!");
            }

            var fee = GetRegistFee(cardId, order.RegType);
            if (fee.Result == Result.成功)
            {
                order.Fee = fee.Response.RegFee.ToDecimalOrDefault();
                order.TreatFee = fee.Response.TreatFee.ToDecimalOrDefault();
                order.OtherFee = 0;
                order.SumFee = order.Fee + order.TreatFee;
            }

            order.CardNo = card.CardNo;
            order.CardType = card.CardType.ToString();
            order.CardName = card.PatientName;
            _ctx.SaveChanges();

            return new ResResult<ApptOrder>(Result.成功, order);
        }

        /// <summary>
        /// 生成其它平台预约订单
        /// </summary>
        /// <returns></returns>
        public ResResult<ApptOrder> CreateOtherApptOrder(int cardId, string deptId, string deptName, string doctorId,
            string doctorName, string regDate, string regType, string timeSlice, string startTime, string endTime,
            decimal fee, decimal treatFee, decimal otherFee, string regNo)
        {
            var card = new BizHospCard(cardId, _openId).Get();

            var existsOrder = _ctx.ApptOrders
                .IsStatus(OrderStatus.预约成功, OrderStatus.支付中, OrderStatus.支付成功, OrderStatus.取号中, OrderStatus.取号成功)
                .FirstOrDefault(x => x.CardNo == card.CardNo
                    && x.OrderType == OrderType.其它平台预约.ToInt32().ToString()
                    && x.RegNo == regNo
                    && x.DoctorId == doctorId
                    && x.RegDate == regDate
                    && x.TimeSlice == timeSlice);
            if (existsOrder != null)
            {
                _orderId = existsOrder.Id;
                if (existsOrder.OrderStatus == OrderStatus.预约成功.ToInt32())
                {
                    return new ResResult<ApptOrder>(Result.成功, existsOrder);
                }
                else if (existsOrder.OrderStatus == OrderStatus.取号成功.ToInt32())
                {
                    return new ResResult<ApptOrder>(Result.失败, existsOrder, "您的订单已成功取号，请到《我的单据》中查看");
                }
                else
                {
                    return new ResResult<ApptOrder>(Result.失败, existsOrder, "系统正在处理您的订单请耐心等候");
                }
            }

            var order = new ApptOrder
            {
                OpenId = _openId,
                OrderType = OrderType.其它平台预约.ToInt32().ToString(), //外平台取号订单
                CardType = card.CardType.ToString(),
                CardNo = card.CardNo,
                CardName = card.PatientName,
                HospitalId = "1036",
                DeptId = deptId,
                DeptName = deptName,
                DoctorId = doctorId,
                DoctorName = doctorName,
                OrderNo = GenerateNewOrderNo(),
                RegDate = regDate,
                RegNo = regNo,
                OrderStatus = OrderStatus.预约成功.ToInt32(), //新订单
                ApptTime = DateTime.Now,
                TimeSlice = timeSlice,
                StartTime = startTime,
                EndTime = endTime,
                RegType = regType,
                PayState = PayState.未付款.ToInt32().ToString(),
                Fee = fee,
                TreatFee = treatFee,
                OtherFee = otherFee,
                SumFee = fee + treatFee + otherFee,
                CreateTime = DateTime.Now
            };

            var feeResult = GetRegistFee(cardId, order.RegType);
            if (feeResult.Result == Result.成功)
            {
                order.Fee = feeResult.Response.RegFee.ToDecimalOrDefault();
                order.TreatFee = feeResult.Response.TreatFee.ToDecimalOrDefault();
                order.OtherFee = 0;
                order.SumFee = order.Fee + order.TreatFee;
            }

            //在挂号单调去HIS接口更新费用后再保存到数据库     
            _ctx.ApptOrders.Add(order);
            _ctx.SaveChanges();
            _orderId = order.Id;

            return new ResResult<ApptOrder>(Result.成功, order);
        }

        /// <summary>
        /// 生成直接挂号订单
        /// </summary>
        /// <returns></returns>
        public ResResult<ApptOrder> CreateDirectApptOrder(int cardId, string deptName, string deptId, string doctorName,
            string doctorId, string apptTreatDate, string timeSlice, string beginTime, string endTime, string regType,
            decimal fee, decimal treatFee, decimal otherFee)
        {
            var card = new BizHospCard(cardId, _openId).Get();

            var order = new ApptOrder
            {
                OpenId = _openId,
                OrderType = OrderType.直接挂号.ToInt32().ToString(), //直接挂号订单
                CardType = card.CardType.ToString(),
                CardNo = card.CardNo,
                CardName = card.PatientName,
                HospitalId = "1036",
                DeptId = deptId,
                DeptName = deptName,
                DoctorId = doctorId,
                DoctorName = doctorName,
                OrderNo = GenerateNewOrderNo(),
                RegDate = apptTreatDate,
                OrderStatus = OrderStatus.未锁号.ToInt32(), //新订单
                TimeSlice = timeSlice,
                StartTime = beginTime,
                EndTime = endTime,
                RegType = regType,
                PayState = PayState.未付款.ToInt32().ToString(),
                Fee = fee,
                TreatFee = treatFee,
                OtherFee = otherFee,
                SumFee = fee + treatFee + otherFee,
                CreateTime = DateTime.Now
            };

            var sameOrder = _ctx.ApptOrders.IsStatus(OrderStatus.取号成功)
                .FirstOrDefault(x =>
                    //x.Id != order.Id&&  //注释by wesley
                    x.CardNo == card.CardNo
                    && x.RegDate == order.RegDate
                    && x.DoctorId == order.DoctorId
                    && x.TimeSlice == order.TimeSlice);

            if (sameOrder != null)
            {
                return new ResResult<ApptOrder>(Result.失败, order, "同个医生同个班次，只能挂号一次!");
            }
            if (order.DeptName.Contains("内科") && (!order.DeptName.Contains("儿")))
            {
                DateTime birthday;
                DateTime.TryParse(card.Birthdate, out birthday);
                int age = DateTime.Now.Date.Year - birthday.Year;
                if (birthday.AddYears(age).CompareTo(DateTime.Now.Date) == -1)
                {
                    age = age - 1;
                }
                if (age < 18)
                {
                    return new ResResult<ApptOrder>(Result.失败, order, "年龄未满18周岁，无法在内科门诊挂号!");
                }
            }

            var cardSameDayAppointMax = ConfigurationManager.AppSettings["CardSameDayAppointMax"].ToInt32();
            var cardApptCount = _ctx.ApptOrders.IsStatus(OrderStatus.取号成功)
                .Count(x => x.CardNo == card.CardNo && x.RegDate == order.RegDate);
            if (cardApptCount >= cardSameDayAppointMax)
            {
                return new ResResult<ApptOrder>(Result.失败, order, "同一天最多可以预约挂号" + cardSameDayAppointMax + "次!");
            }

            var feeResult = GetRegistFee(cardId, order.RegType);
            if (feeResult.Result == Result.成功)
            {
                order.Fee = feeResult.Response.RegFee.ToDecimalOrDefault();
                order.TreatFee = feeResult.Response.TreatFee.ToDecimalOrDefault();
                order.OtherFee = 0;
                order.SumFee = order.Fee + order.TreatFee;
            }

            //在挂号单调用HIS更新挂号费用后再提交保存到数据库
            _ctx.ApptOrders.Add(order);
            _ctx.SaveChanges();
            _orderId = order.Id;

            return new ResResult<ApptOrder>(Result.成功, order);
        }

        /// <summary>
        /// 获取挂号费用
        /// </summary>
        /// <param name="cardId">就诊卡id</param>
        /// <param name="regType">挂号类型</param>
        /// <returns></returns>
        public ResResult<GetRegistFeeResponse> GetRegistFee(int cardId, string regType)
        {
            var card = new BizHospCard(cardId, _openId).Get();

            //执行获取挂号费操作
            var request = new GetRegistFeeRequest();
            request.CardNo = card.CardNo;
            request.RegType = regType;

            GetRegistFeeResponse response;
            try
            {
                var httpClient = InHttpClient.GetClient();
                var responseMsg = httpClient.PostAsJsonAsync("HisWs/GetRegistFee", request).Result;
                responseMsg.EnsureSuccessStatusCode();
                response = responseMsg.Content.ReadAsAsync<GetRegistFeeResponse>().Result;
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("调用院内获取挂号费Api出错,信息：{0}", JsonConvert.SerializeObject(ex));
                return new ResResult<GetRegistFeeResponse>(Result.未返回);
            }

            if (response.ResultCode != "0")
            {
                return new ResResult<GetRegistFeeResponse>(Result.失败, response, response.ResultDesc);
            }
            else
            {
                return new ResResult<GetRegistFeeResponse>(Result.成功, response, response.ResultDesc);
            }
        }

        /// <summary>
        /// 执行锁号 前提：订单状态为 未锁定/新建 (0)
        /// </summary>
        /// <param name="autoFeedback">是否自动做成功/失败数据处理 （不自动处理可支持失败后重试）</param>
        /// <returns></returns>
        public ResResult<ExeLockSourceResponse> Locking(bool autoFeedback = true)
        {
            var result = new ResResult<ExeLockSourceResponse>();

            //更新订单状态为 锁号中(1) 【前提：订单状态为 未锁定/新建 (0)】            
            var order = _ctx.ApptOrders.IsOpenId(_openId).IsStatus(OrderStatus.未锁号)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = (int)OrderStatus.锁号中;
                _ctx.SaveChanges();
            }
            else
            {
                result.Result = Result.条件不足未执行;
                return result;
            }

            //执行锁号操作
            var request = new ExeLockSourceRequest();
            request.OrderId = order.OrderNo;
            request.DeptId = order.DeptId;
            request.DoctorId = order.DoctorId;
            request.RegDate = order.RegDate;
            request.TimeSlice = order.TimeSlice;
            request.RegType = order.RegType;
            request.StartTime = order.StartTime;
            request.EndTime = order.EndTime;
            request.Fee = order.Fee.GetValueOrDefault().ToString("F2");
            request.TreatFee = order.TreatFee.GetValueOrDefault().ToString("F2");
            request.OtherFee = order.OtherFee.GetValueOrDefault().ToString("F2");
            request.SumFee = order.SumFee.GetValueOrDefault().ToString("F2");
            request.SqNo = order.Sqno;

            ExeLockSourceResponse response;
            try
            {
                var httpClient = InHttpClient.GetClient();
                var responseMsg = httpClient.PostAsJsonAsync("HisWs/ExeLockSource", request).Result;
                responseMsg.EnsureSuccessStatusCode();
                response = responseMsg.Content.ReadAsAsync<ExeLockSourceResponse>().Result;
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("调用院内锁号Api出错,信息：{0}", JsonConvert.SerializeObject(ex));
                // 未返回当失败处理
                if (autoFeedback)
                {
                    LockFailed();
                }
                result.Result = Result.未返回;
                return result;
            }

            result.Response = response;

            //锁号成功
            if (response.ResultCode == "0")
            {
                if (autoFeedback)
                {
                    LockSucceed();
                }
                result.Result = Result.成功;
                return result;
            }
            //锁号失败
            else
            {
                if (autoFeedback)
                {
                    LockFailed();
                }
                result.Result = Result.失败;
                result.Message = response.ResultDesc;
                return result;
            }
        }

        /// <summary>
        /// 锁号成功的数据处理 前提：订单状态为 锁号中（1）
        /// </summary>
        public void LockSucceed()
        {
            //更新订单状态为 锁号成功(3) 【前提：订单状态为 锁号中（1）】           
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.锁号中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = (int)OrderStatus.锁号成功;
                order.LockTime = DateTime.Now;
                _ctx.SaveChanges();

                // RMQ - 60秒后如果还是锁号成功状态则执行取消锁号
                var unlockingProducer = new OrderUnlockingProducer();
                unlockingProducer.Publish(new OrderUnlockingMsg
                {
                    OrderType = Rmq.Center.OrderType.挂号订单,
                    OrderId = order.Id,
                    OrderStatus = new[] { OrderStatus.锁号成功.ToInt32() }
                }, 60 * 1000);
            }
        }

        /// <summary>
        /// 锁号失败的数据处理 前提：订单状态为 锁号中（1）
        /// </summary>
        public void LockFailed()
        {
            //更新订单状态为 锁号失败(2) 【前提：订单状态为 锁号中（1）】
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.锁号中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = (int)OrderStatus.锁号失败;
                _ctx.SaveChanges();
            }
        }

        /// <summary>
        /// 执行取消锁号 前提： 订单状态为 锁号成功（3） 或 预约失败 (9)
        /// </summary>
        /// <param name="autoFeedback">是否自动做成功/失败数据处理</param>
        /// <returns></returns>
        public ResResult<ExeUnLockSourceResponse> Unlocking(bool autoFeedback = true)
        {
            //更新订单状态为 取消锁号中(4) 【前提：订单状态为 锁号成功(3) 或 预约失败 (9)】
            var order = _ctx.ApptOrders.IsOpenId(_openId).IsStatus(OrderStatus.锁号成功, OrderStatus.预约失败)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                //把autofeedback状态处理提前到订单状态更新之前,把我们的订单状态处于一个前进的形式，使状态容易看的懂
                if (order.OrderStatus != OrderStatus.锁号成功.ToInt32())
                {
                    autoFeedback = false;
                }

                if (autoFeedback)
                {
                    order.OrderStatus = OrderStatus.取消锁号中.ToInt32();
                    _ctx.SaveChanges();
                }
            }
            else
            {
                return new ResResult<ExeUnLockSourceResponse>(Result.条件不足未执行);
            }


            //执行取消锁号操作
            var request = new ExeUnLockSourceRequest();
            request.OrderId = order.OrderNo;

            ExeUnLockSourceResponse response;
            try
            {
                var httpClient = InHttpClient.GetClient();
                var responseMsg = httpClient.PostAsJsonAsync("HisWs/ExeUnLockSource", request).Result;
                responseMsg.EnsureSuccessStatusCode();
                response = responseMsg.Content.ReadAsAsync<ExeUnLockSourceResponse>().Result;
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("调用院内取消锁号Api出错,信息：{0}", JsonConvert.SerializeObject(ex));
                // 未返回当失败处理
                if (autoFeedback)
                {
                    UnlockFailed();
                }
                return new ResResult<ExeUnLockSourceResponse>(Result.未返回);
            }

            //取消锁号成功
            if (response.ResultCode == "0")
            {
                if (autoFeedback)
                {
                    UnlockSucceed();
                }

                return new ResResult<ExeUnLockSourceResponse>(Result.成功, response);
            }
            //取消锁号失败
            else
            {
                if (autoFeedback)
                {
                    UnlockFailed();
                }

                return new ResResult<ExeUnLockSourceResponse>(Result.失败, response.ResultDesc);
            }
        }

        /// <summary>
        /// 取消锁号成功的数据处理 前提：订单状态为 取消锁号中（4）
        /// </summary>
        public void UnlockSucceed()
        {
            // 更新订单状态为 取消锁号成功(5)【前提：订单状态为 取消锁号中（4）】
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.取消锁号中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = OrderStatus.取消锁号成功.ToInt32();
                _ctx.SaveChanges();
            }
        }

        /// <summary>
        /// 取消锁号失败的数据处理 前提：订单状态为 取消锁号中（4）
        /// </summary>
        public void UnlockFailed()
        {
            // 更新订单状态为 取消锁号失败(6) 【前提：订单状态为 取消锁号中（4）】
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.取消锁号中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = OrderStatus.取消锁号失败.ToInt32();
                _ctx.SaveChanges();
            }
        }

        /// <summary>
        /// 执行预约，前提：订单状态为 锁号成功（3）
        /// </summary>
        /// <param name="autoFeedback">是否自动做成功/失败数据处理</param>
        /// <returns></returns>
        public ResResult<ExeAppointResponse> Appting(bool autoFeedback = true)
        {
            var order = _ctx.ApptOrders.IsOpenId(_openId).IsStatus(OrderStatus.锁号成功)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = OrderStatus.预约中.ToInt32();
                _ctx.SaveChanges();
            }
            else
            {
                return new ResResult<ExeAppointResponse>(Result.条件不足未执行);
            }

            //执行预约操作
            var request = new ExeAppointRequest();
            request.OrderId = order.OrderNo;
            request.CardNo = order.CardNo;

            ExeAppointResponse response;
            try
            {
                var httpClient = InHttpClient.GetClient();
                var responseMsg = httpClient.PostAsJsonAsync("HisWs/ExeAppoint", request).Result;
                responseMsg.EnsureSuccessStatusCode();
                response = responseMsg.Content.ReadAsAsync<ExeAppointResponse>().Result;
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("调用院内预约Api出错,信息：{0}", JsonConvert.SerializeObject(ex));
                // 未返回当失败处理
                if (autoFeedback)
                {
                    ApptFailed();
                }
                return new ResResult<ExeAppointResponse>(Result.未返回);
            }

            if (response.ResultCode != "0")
            {
                if (autoFeedback)
                {
                    ApptFailed();
                }
                return new ResResult<ExeAppointResponse>(Result.失败, response, response.ResultDesc);
            }
            else
            {
                if (autoFeedback)
                {
                    ApptSucceed();
                }
                return new ResResult<ExeAppointResponse>(Result.成功, response);
            }
        }

        /// <summary>
        /// 预约成功的数据操作 前提：订单状态为 预约中(7)
        /// </summary>
        public void ApptSucceed()
        {
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.预约中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = (int)OrderStatus.预约成功;
                order.ApptTime = DateTime.Now;
                _ctx.SaveChanges();

                // RMQ - 16分钟后做预约成功超时检查
                // TODO 该成功检查无意义，预约成功后会立即调用支付，支付中检查会保证不超时
                //var unapptingProducer = new OrderUnapptingProducer();
                //unapptingProducer.Publish(new OrderUnapptingMsg
                //{
                //    OrderType = Rmq.Center.OrderType.挂号订单,
                //    OrderId = order.Id,
                //    OrderStatus = new[] { OrderStatus.预约成功.ToInt32() }
                //}, 16 * 60 * 1000);
            }
        }

        /// <summary>
        /// 预约失败的数据处理 前提：订单状态为 预约中(7)
        /// </summary>
        public void ApptFailed()
        {
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.预约中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = (int)OrderStatus.预约失败;
                _ctx.SaveChanges();

                // RMQ - 预约失败则立即执行取消锁号
                var unlockingProducer = new OrderUnlockingProducer();
                unlockingProducer.Publish(new OrderUnlockingMsg
                {
                    OrderType = Rmq.Center.OrderType.挂号订单,
                    OrderId = order.Id,
                    OrderStatus = new[] { OrderStatus.预约失败.ToInt32() }
                });
            }
        }

        /// <summary>
        /// 执行取消预约
        /// 前提：订单状态为 预约成功（8） 或 退款中（18） 退款成功 （19） 退款失败（20） 或 支付失败 (14) 或 取号失败 (17)
        /// </summary>
        /// <param name="reason">取消原因</param>
        /// <param name="autoFeedback">是否自动做成功/失败数据处理</param>
        /// <returns></returns>
        public ResResult<CancelAppointResponse> Unappting(UnapptingReason reason = UnapptingReason.系统取消,
            bool autoFeedback = true)
        {
            var order = _ctx.ApptOrders.IsOpenId(_openId).IsStatus(OrderStatus.预约成功, OrderStatus.退款中,
                OrderStatus.退款成功, OrderStatus.退款失败, OrderStatus.支付失败, OrderStatus.取号失败)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null && order.OrderType == OrderType.预约.ToInt32().ToString())
            {
                //把autofeedback状态处理提前到订单状态更新之前,把我们的订单状态处于一个前进的形式，使状态容易看的懂
                if (order.OrderStatus != OrderStatus.预约成功.ToInt32())
                {
                    autoFeedback = false;
                }

                if (autoFeedback)
                {
                    order.OrderStatus = OrderStatus.取消预约中.ToInt32();
                    _ctx.SaveChanges();
                }
            }
            else
            {
                return new ResResult<CancelAppointResponse>(Result.条件不足未执行);
            }

            var request = new CancelAppointRequest();
            request.OrderId = order.OrderNo;
            CancelAppointResponse response;
            try
            {
                var httpClient = InHttpClient.GetClient();
                var responseMsg = httpClient.PostAsJsonAsync("HisWs/CancelAppoint", request).Result;
                responseMsg.EnsureSuccessStatusCode();
                response = responseMsg.Content.ReadAsAsync<CancelAppointResponse>().Result;
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("调用院内取消预约Api出错,信息：{0}", JsonConvert.SerializeObject(ex));
                // 未返回当失败处理
                if (autoFeedback)
                {
                    UnapptFailed();
                }
                return new ResResult<CancelAppointResponse>(Result.未返回);
            }

            if (response.ResultCode != "0")
            {
                if (autoFeedback)
                {
                    UnapptFailed();
                }
                return new ResResult<CancelAppointResponse>(Result.失败, response, response.ResultDesc);
            }
            else
            {
                if (autoFeedback)
                {
                    UnapptSucceed(reason);
                }
                return new ResResult<CancelAppointResponse>(Result.成功, response);
            }
        }

        public enum UnapptingReason
        {
            用户取消 = 1,
            系统取消 = 2,
            医院停诊取消 = 3
        }

        /// <summary>
        /// 取消预约成功的数据处理，前提：订单状态为 取消预约中(10)
        /// </summary>
        public void UnapptSucceed(UnapptingReason reason)
        {
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.取消预约中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = OrderStatus.取消预约成功.ToInt32();
                if (reason == UnapptingReason.用户取消)
                {
                    order.CancelByUserTime = DateTime.Now;
                }
                if (reason == UnapptingReason.系统取消)
                {
                    order.CancelBySysTime = DateTime.Now;
                }
                if (reason == UnapptingReason.医院停诊取消)
                {
                    order.CancelByHospTime = DateTime.Now;
                }
                _ctx.SaveChanges();
            }
        }

        /// <summary>
        /// 取消预约失败的数据处理，前提：订单状态为 取消预约中(10)
        /// </summary>
        public void UnapptFailed()
        {
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.取消预约中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = OrderStatus.取消预约失败.ToInt32();
                _ctx.SaveChanges();
            }
        }

        /// <summary>
        /// 执行支付,(前提： 订单状态为 预约成功（8） 或者 是直接挂号 （orderType="2" & orderStatus = 0））
        /// </summary>
        /// <param name="clientIp">客户ip</param>
        /// <returns></returns>
        public ResResult<string> Paying(string clientIp)
        {
            var order = _ctx.ApptOrders.FirstOrDefault(
                        x => x.Id == _orderId && (x.OrderStatus == 8 || (x.OrderType == "2" && x.OrderStatus == 0)));
            if (order != null)
            {
                order.OrderStatus = OrderStatus.支付中.ToInt32();
                _ctx.SaveChanges();
                // 费用为0时直接设置为支付成功
                if (order.SumFee == 0)
                {
                    PaySucceed(DateTime.Now, "0");
                    return new ResResult<string>(Result.成功, "", "");
                }
            }
            else
            {
                return new ResResult<string>(Result.条件不足未执行);
            }

            // RMQ 16分钟后执行支付失败操作
            var payFailedProducer = new OrderPayFailedProducer();
            payFailedProducer.Publish(new OrderPayFailedMsg
            {
                OrderType = Rmq.Center.OrderType.挂号订单,
                OrderId = order.Id,
                OrderStatus = new[] { OrderStatus.支付中.ToInt32() }
            }, 16 * 60 * 1000);

            //组织 微信 js 支付方法 getBrandWCPayRequest 的参数
            var payStartTime = System.DateTime.Now.ToString("yyyyMMddHHmmss");
            var payExpireTime = System.DateTime.Now.AddMinutes(15).ToString("yyyyMMddHHmmss");

            var payFee = (order.SumFee * 100).ToInt32().ToString();
            var superOpenIds = ConfigurationManager.AppSettings["OpenIdsForPayMin"]
                .Split(" , ", StringSplitOptions.RemoveEmptyEntries);
            if (superOpenIds.Contains(order.OpenId))
            {
                payFee = "1";
            }

            var payRequestParam = TenpayV2Helper.GenerateWxPayParameter("就诊挂号", "AP" + order.OrderNo,
                payFee, clientIp, payStartTime, payExpireTime);

            return new ResResult<string>(Result.成功, payRequestParam, "");
        }

        ///  <summary>
        ///  支付成功的处理
        ///  </summary>
        /// <param name="payDt">支付成功时间</param>
        /// <param name="transactionId">支付交易号</param>
        public void PaySucceed(DateTime payDt, string transactionId)
        {
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.支付中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.PayState = PayState.已付款.ToInt32().ToString();
                order.PayTime = payDt;
                order.WxTransactionId = transactionId;
                order.OrderStatus = OrderStatus.支付成功.ToInt32();
                _ctx.SaveChanges();

                // RMQ 执行取号操作
                var pickupingProducer = new OrderPickupingProducer();
                pickupingProducer.Publish(new OrderPickupingMsg
                {
                    OrderType = Rmq.Center.OrderType.挂号订单,
                    OrderId = order.Id,
                    ApptOrderType = order.OrderType,
                    OrderStatus = new[] { OrderStatus.支付成功.ToInt32() }
                });
            }
        }

        /// <summary>
        /// 执行取消支付
        /// </summary>
        /// <returns></returns>
        public void PayFailed()
        {
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.支付中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = OrderStatus.支付失败.ToInt32();
                _ctx.SaveChanges();

                if (order.OrderType == OrderType.预约.ToInt32().ToString())
                {
                    // RMQ 执行取消预约操作
                    var unapptingProducer = new OrderUnapptingProducer();
                    unapptingProducer.Publish(new OrderUnapptingMsg
                    {
                        OrderType = Rmq.Center.OrderType.挂号订单,
                        OrderId = order.Id,
                        OrderStatus = new[] { OrderStatus.支付失败.ToInt32() }
                    });
                }
            }
        }

        /// <summary>
        /// 执行取号 (预约挂号 取号)
        /// </summary>
        /// <param name="autoFeedback">是否自动做成功/失败数据处理</param>
        /// <returns></returns>
        public ResResult<ExeRegisterResponse> PickupingAppt(bool autoFeedback = true)
        {
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.支付成功)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = OrderStatus.取号中.ToInt32();
                _ctx.SaveChanges();
            }
            else
            {
                return new ResResult<ExeRegisterResponse>(Result.条件不足未执行);
            }

            var refundOpenIds = ConfigurationManager.AppSettings["OpenIdsForMustRefund"].Split(" , ",
                            StringSplitOptions.RemoveEmptyEntries);
            var isFromRefundOpenId = refundOpenIds.Contains(order.OpenId);
            if (isFromRefundOpenId)
            {
                if (autoFeedback)
                {
                    PickupFailed();
                }
                return new ResResult<ExeRegisterResponse>(Result.失败,
                    new ExeRegisterResponse
                    {
                        ResultCode = "-1",
                        ResultDesc = "必退款测试人员"
                    }, "必退款测试人员");
            }

            var request = new ExeRegisterRequest();
            request.OrderId = order.OrderNo;
            request.PayType = "0";
            request.CardNo = order.CardNo;
            request.PayCount = order.SumFee.ToString();
            request.TransNo = order.WxTransactionId;
            request.TransTime = order.PayTime.GetValueOrDefault().ToString("yyyy-MM-dd HH:mm:ss");

            ExeRegisterResponse response;
            try
            {
                var httpClient = InHttpClient.GetClient();
                var responseMsg = httpClient.PostAsJsonAsync("HisWs/ExeRegister", request).Result;
                responseMsg.EnsureSuccessStatusCode();
                response = responseMsg.Content.ReadAsAsync<ExeRegisterResponse>().Result;
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("调用院内取号Api出错,信息：{0}", JsonConvert.SerializeObject(ex));
                // RMQ 执行取号结果查询
                var pickupingChkProducer = new OrderPickupingChkProducer();
                pickupingChkProducer.Publish(new OrderPickupingChkMsg
                {
                    OrderType = Rmq.Center.OrderType.挂号订单,
                    OrderId = order.Id,
                    OrderStatus = new[] { OrderStatus.取号中.ToInt32() }
                });
                return new ResResult<ExeRegisterResponse>(Result.未返回);
            }

            if (response.ResultCode != "0")
            {
                if (autoFeedback)
                {
                    PickupFailed();
                }
                return new ResResult<ExeRegisterResponse>(Result.失败, response, response.ResultDesc);
            }
            else
            {
                if (autoFeedback)
                {
                    PickupSucceed(response.RegNo, response.BeginTime, response.EndTime, response.Address,
                        response.VisitNo);
                }
                return new ResResult<ExeRegisterResponse>(Result.成功, response);
            }
        }

        /// <summary>
        /// 执行取号 (直接挂号/其他平台预约缴费 取号)
        /// </summary>
        /// <param name="autoFeedback">是否自动做成功/失败数据处理</param>
        /// <returns></returns>
        public ResResult<ExeResSourceRegisterResponse> PickupingOther(bool autoFeedback = true)
        {
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.支付成功)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = OrderStatus.取号中.ToInt32();
                _ctx.SaveChanges();
            }
            else
            {
                return new ResResult<ExeResSourceRegisterResponse>(Result.条件不足未执行);
            }

            //执行取号操作
            var request = new ExeResSourceRegisterRequest();
            request.OrderId = order.OrderNo;
            request.PayType = order.OrderType == "1" ? "2" : "1";
            request.CardNo = order.CardNo;
            request.PayCount = order.SumFee.ToString();
            request.TransNo = order.WxTransactionId;
            request.TransTime = order.PayTime.GetValueOrDefault().ToString("yyyy-MM-dd HH:mm:ss");
            request.DeptId = order.DeptId;
            request.DoctorId = order.DoctorId;
            request.RegDate = order.RegDate;
            request.TimeSlice = order.TimeSlice;
            request.RegType = order.RegType;
            request.StartTime = order.StartTime;
            request.EndTime = order.EndTime;
            request.Fee = order.Fee.GetValueOrDefault().ToString();
            request.TreatFee = order.TreatFee.GetValueOrDefault().ToString();
            request.OtherFee = order.OtherFee.GetValueOrDefault().ToString();
            request.RegNo = order.RegNo ?? "";

            ExeResSourceRegisterResponse response;

            try
            {
                var httpClient = InHttpClient.GetClient();
                var responseMsg = httpClient.PostAsJsonAsync("HisWs/ExeResSourceRegister", request).Result;
                responseMsg.EnsureSuccessStatusCode();
                response = responseMsg.Content.ReadAsAsync<ExeResSourceRegisterResponse>().Result;
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("调用院内(直接挂号/其它平台预约)取号Api出错,信息：{0}", JsonConvert.SerializeObject(ex));
                // RMQ 执行取号结果查询
                var pickupingChkProducer = new OrderPickupingChkProducer();
                pickupingChkProducer.Publish(new OrderPickupingChkMsg
                {
                    OrderType = Rmq.Center.OrderType.挂号订单,
                    OrderId = order.Id,
                    OrderStatus = new[] { OrderStatus.取号中.ToInt32() }
                });
                return new ResResult<ExeResSourceRegisterResponse>(Result.未返回);
            }

            if (response.ResultCode != "0")
            {
                if (autoFeedback)
                {
                    PickupFailed();
                }
                return new ResResult<ExeResSourceRegisterResponse>(Result.失败, response, response.ResultDesc);
            }
            else
            {
                if (autoFeedback)
                {
                    PickupSucceed(response.RegNo, response.BeginTime, response.EndTime, response.Address,
                        response.VisitNo);
                }
                return new ResResult<ExeResSourceRegisterResponse>(Result.成功, response);
            }
        }

        /// <summary>
        /// 执行挂号订单状态查询操作
        /// </summary>
        /// <param name="autoFeedback">是否自动做成功/失败数据处理</param>
        /// <returns></returns>
        public ResResult<GetOrderStatusResponse> PickupingChk(bool autoFeedback = true)
        {
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.取号中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order == null)
            {
                return new ResResult<GetOrderStatusResponse>(Result.条件不足未执行);
            }

            //执行查询挂号状态操作
            var request = new GetOrderStatusRequest();
            request.OrderId = order.OrderNo;

            GetOrderStatusResponse response;
            try
            {
                var httpClient = InHttpClient.GetClient();
                var responseMsg = httpClient.PostAsJsonAsync("HisWs/GetOrderStatus", request).Result;
                responseMsg.EnsureSuccessStatusCode();
                response = responseMsg.Content.ReadAsAsync<GetOrderStatusResponse>().Result;
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("调用院内查询取号状态Api出错,信息：{0}", JsonConvert.SerializeObject(ex));
                return new ResResult<GetOrderStatusResponse>(Result.未返回);
            }

            if (response.ResultCode != "0")
            {
                if (autoFeedback)
                {
                    PickupFailed();
                }
                return new ResResult<GetOrderStatusResponse>(Result.失败, response, response.ResultDesc);
            }
            else
            {
                // 查询结果为已取号
                if (response.Status == "0")
                {
                    if (autoFeedback)
                    {
                        PickupSucceed(response.RegNo, response.BeginTime, response.EndTime, response.Address,
                            response.VisitNo);
                    }
                }
                // 查询结果为未取号
                if (response.Status == "1")
                {
                    if (autoFeedback)
                    {
                        PickupFailed();
                    }
                }
                return new ResResult<GetOrderStatusResponse>(Result.成功, response);
            }
        }

        /// <summary>
        /// 取号成功的处理
        /// </summary>
        /// <param name="regNo">就诊序号</param>
        /// <param name="suggestBeginTime">建议就诊开始时间</param>
        /// <param name="suggestEndTime">建议就诊结束时间</param>
        /// <param name="location">就诊地点</param>
        /// <param name="visitNo">就诊流水号</param>
        public void PickupSucceed(string regNo, string suggestBeginTime, string suggestEndTime, string location, string visitNo)
        {
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.取号中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = OrderStatus.取号成功.ToInt32();
                order.RegNo = regNo;
                order.SuggestBeginTime = suggestBeginTime;
                order.SuggestEndTime = suggestEndTime;
                order.Location = location;
                order.VisitNo = visitNo;

                order.FetchTime = DateTime.Now;
                _ctx.SaveChanges();
                // TODO RMQ 发送用户挂号成功通知
                SendPickupSucceedNotice();

                // TODO RMQ 发送发货通知
                if (order.SumFee > 0)
                {
                    TenpayV2Helper.DoDeliverNotify(order.OpenId, order.WxTransactionId,
                        "AP" + order.OrderNo, "1", "");
                }
            }
        }

        /// <summary>
        /// 取号失败的处理
        /// </summary>
        public void PickupFailed()
        {
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.取号中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = OrderStatus.取号失败.ToInt32();
                //新增保存订单状态
                _ctx.SaveChanges();

                if (order.OrderType == OrderType.预约.ToInt32().ToString())
                {
                    var unapptingProducer = new OrderUnapptingProducer();
                    unapptingProducer.Publish(new OrderUnapptingMsg
                    {
                        OrderType = Rmq.Center.OrderType.挂号订单,
                        OrderId = order.Id,
                        Reason = 2,
                        OrderStatus = new[] { OrderStatus.取号失败.ToInt32() }
                    });
                }

                SendPickupFailedNotice();

                if (order.SumFee > 0)
                {
                    // RMQ 执行退费操作
                    var refundingProducer = new OrderRefundingProducer();
                    refundingProducer.Publish(new OrderRefundingMsg
                    {
                        OrderType = Rmq.Center.OrderType.挂号订单,
                        OrderId = order.Id,
                        Reason = 1,
                        OrderStatus = new[] { OrderStatus.取号失败.ToInt32() }
                    }, 1000);
                }
            }
        }

        /// <summary>
        /// 执行退费操作
        /// </summary>
        /// <param name="reason">退费原因</param>
        /// <param name="autoFeedback">是否自动做成功/失败数据处理</param>
        /// <returns></returns>
        public ResResult<RefundResult> Refunding(RefundReason reason, bool autoFeedback = true)
        {
            var order = _ctx.ApptOrders.FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                if (reason == RefundReason.交易失败)
                {
                    order.OrderStatus = OrderStatus.退款中.ToInt32();
                }
                else
                {
                    order.OrderStatus = OrderStatus.微信退号退款中.ToInt32();
                }
                order.PayState = PayState.退款中.ToInt32().ToString();
                order.RefundReason = reason.ToString();

                _ctx.SaveChanges();
            }
            else
            {
                return new ResResult<RefundResult>(Result.条件不足未执行);
            }

            RefundResult refundResult;
            try
            {
                refundResult = TenpayV2Helper.DoRefund(order.OpenId, "AP" + order.OrderNo, order.WxTransactionId,
                "AP" + order.OrderNo, (order.SumFee * 100).ToInt32(), (order.SumFee * 100).ToInt32());
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("调用第三方支付退款Api出错,信息：{0}", JsonConvert.SerializeObject(ex));
                return new ResResult<RefundResult>(Result.未返回);
            }
            //退款成功 4,10
            //退款处理中 8 9 11
            if (refundResult.RefundStatus == 4 || refundResult.RefundStatus == 10 ||
                refundResult.RefundStatus == 8 || refundResult.RefundStatus == 9 || refundResult.RefundStatus == 11)
            {
                if (autoFeedback)
                {
                    RefundSucceed(refundResult, reason);
                }

                return new ResResult<RefundResult>(Result.成功, refundResult);
            }
            //退款失败 3，5，6
            //转入代发 7
            if (refundResult.RefundStatus == 7 ||
                refundResult.RefundStatus == 3 || refundResult.RefundStatus == 5 || refundResult.RefundStatus == 6)
            {
                if (autoFeedback)
                {
                    RefundFailed(reason);
                }
                return new ResResult<RefundResult>(Result.失败, refundResult, "");
            }

            return new ResResult<RefundResult>(Result.未返回);
        }

        public enum RefundReason
        {
            交易失败 = 1,
            用户取消交易 = 2
        }

        /// <summary>
        /// 退费成功的处理
        /// </summary>
        /// <param name="resultResult"></param>
        /// <param name="reason"></param>
        public void RefundSucceed(RefundResult resultResult, RefundReason reason)
        {
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.退款中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                if (reason == RefundReason.交易失败)
                {
                    order.OrderStatus = OrderStatus.退款成功.ToInt32();
                }
                else
                {
                    order.OrderStatus = OrderStatus.微信退号退款成功.ToInt32();
                }

                order.RefundReason = reason.ToString();
                order.PayState = PayState.已退款.ToInt32().ToString();
                order.RefundTime = DateTime.Now;
                order.WxRefundId = resultResult.RefundId;
                _ctx.SaveChanges();

                //TODO RMQ 发送用户退费成功通知
                //TODO RMQ 发送发货通知，发货失败
            }
        }

        /// <summary>
        /// 退费失败的处理
        /// </summary>
        /// <param name="reason"></param>
        public void RefundFailed(RefundReason reason)
        {
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.退款中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                if (reason == RefundReason.交易失败)
                {
                    order.OrderStatus = OrderStatus.退款失败.ToInt32();
                }
                else
                {
                    order.OrderStatus = OrderStatus.微信退号退款失败.ToInt32();
                }
                order.PayState = PayState.退款失败.ToInt32().ToString();
                _ctx.SaveChanges();

                //TODO RMQ 发送发货通知，发货失败
            }
        }

        /// <summary>
        /// 执行退号操作
        /// </summary>
        /// <param name="autoFeedback">是否自动做成功/失败数据处理</param>
        /// <returns></returns>
        public ResResult<CancelRegisterResponse> Unpickuping(bool autoFeedback)
        {
            var order = _ctx.ApptOrders.IsOpenId(_openId).IsStatus(OrderStatus.取号成功)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null && order.OrderType == OrderType.预约.ToInt32().ToString()
                && order.RegDate.ToDateTime() > DateTime.Now)
            {
                order.OrderStatus = OrderStatus.微信退号中.ToInt32();
                _ctx.SaveChanges();
            }
            else
            {
                return new ResResult<CancelRegisterResponse>(Result.条件不足未执行);
            }

            //执行退号操作
            var request = new CancelRegisterRequest();
            request.OrderId = order.OrderNo;

            CancelRegisterResponse response;

            try
            {
                var httpClient = InHttpClient.GetClient();
                var responseMsg = httpClient.PostAsJsonAsync("HisWs/CancelRegister", request).Result;
                responseMsg.EnsureSuccessStatusCode();
                response = responseMsg.Content.ReadAsAsync<CancelRegisterResponse>().Result;
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("调用院内退号Api出错,信息：{0}", JsonConvert.SerializeObject(ex));
                // RMQ 执行退号结果查询
                var unpickupingChkProducer = new OrderUnpickupingChkProducer();
                unpickupingChkProducer.Publish(new OrderUnpickupingChkMsg
                {
                    OrderType = Rmq.Center.OrderType.挂号订单,
                    OrderId = order.Id,
                    OrderStatus = new[] { OrderStatus.微信退号中.ToInt32() }
                });
                return new ResResult<CancelRegisterResponse>(Result.未返回);
            }

            if (response.ResultCode != "0")
            {
                if (autoFeedback)
                {
                    UnpickupFailed();
                }
                return new ResResult<CancelRegisterResponse>(Result.失败, response, response.ResultDesc);
            }
            else
            {
                if (autoFeedback)
                {
                    UnpickupSucceed();
                }
                return new ResResult<CancelRegisterResponse>(Result.成功, response);
            }
        }

        /// <summary>
        /// 退号成功的操作
        /// </summary>
        public void UnpickupSucceed()
        {
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.微信退号中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                order.OrderStatus = OrderStatus.微信退号成功.ToInt32();
                _ctx.SaveChanges();

                // TODO 发送退号成功的用户通知

                if (order.SumFee > 0)
                {
                    // RMQ 执行退费操作
                    var refundingProducer = new OrderRefundingProducer();
                    refundingProducer.Publish(new OrderRefundingMsg
                    {
                        OrderType = Rmq.Center.OrderType.挂号订单,
                        OrderId = order.Id,
                        Reason = 2,
                        OrderStatus = new[] { OrderStatus.微信退号成功.ToInt32() }
                    }, 1000);
                }
            }
        }

        /// <summary>
        /// 退号失败的操作
        /// </summary>
        public void UnpickupFailed()
        {
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.微信退号中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                // order.OrderStatus = OrderStatus.微信退号失败.ToInt32();
                order.OrderStatus = OrderStatus.取号成功.ToInt32();
                _ctx.SaveChanges();

                // TODO 发送退号失败的用户通知
            }
        }

        /// <summary>
        /// 执行退号订单状态查询操作
        /// </summary>
        /// <param name="autoFeedback">是否自动做成功/失败数据处理</param>
        /// <returns></returns>
        public ResResult<GetOrderStatusResponse> UnpickupingChk(bool autoFeedback = true)
        {
            var order = _ctx.ApptOrders.IsStatus(OrderStatus.微信退号中)
                .FirstOrDefault(x => x.Id == _orderId);
            if (order == null)
            {
                return new ResResult<GetOrderStatusResponse>(Result.条件不足未执行);
            }

            //执行查询挂号状态操作
            var request = new GetOrderStatusRequest();
            request.OrderId = order.OrderNo;

            GetOrderStatusResponse response;
            try
            {
                var httpClient = InHttpClient.GetClient();
                var responseMsg = httpClient.PostAsJsonAsync("HisWs/GetOrderStatus", request).Result;
                responseMsg.EnsureSuccessStatusCode();
                response = responseMsg.Content.ReadAsAsync<GetOrderStatusResponse>().Result;
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("调用院内查询取号状态Api出错,信息：{0}", JsonConvert.SerializeObject(ex));
                return new ResResult<GetOrderStatusResponse>(Result.未返回);
            }

            if (response.ResultCode != "0")
            {
                if (autoFeedback)
                {
                    UnpickupFailed();
                }
                return new ResResult<GetOrderStatusResponse>(Result.失败, response, response.ResultDesc);
            }
            else
            {
                // 查询结果为已退号
                if (response.Status == "2")
                {
                    if (autoFeedback)
                    {
                        UnpickupSucceed();
                    }
                }
                // 查询结果为已取号
                if (response.Status == "0")
                {
                    if (autoFeedback)
                    {
                        UnpickupFailed();
                    }
                }
                return new ResResult<GetOrderStatusResponse>(Result.成功, response);
            }
        }

        /// <summary>
        /// 发送取号成功的用户通知
        /// </summary>
        public void SendPickupSucceedNotice()
        {
            var order = _ctx.ApptOrders.FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                CustomApi.SendText(null, order.OpenId, "您已挂号成功");
            }
        }

        /// <summary>
        /// 发送取号失败的用户通知
        /// </summary>
        public void SendPickupFailedNotice()
        {
            var order = _ctx.ApptOrders.FirstOrDefault(x => x.Id == _orderId);
            if (order != null)
            {
                CustomApi.SendText(null, order.OpenId, "您已挂号失败");
            }
        }

        /// <summary>
        /// 资源释放
        /// </summary>
        public void Dispose()
        {
            _ctx.Dispose();
        }
    }

    public static class ApptOrderExtensions
    {
        public static IQueryable<ApptOrder> IsOpenId(this IQueryable<ApptOrder> source, string openId)
        {
            if (string.IsNullOrEmpty(openId))
            {
                return source;
            }

            return source.Where(x => x.OpenId == openId);
        }

        public static IQueryable<ApptOrder> IsStatus(this IQueryable<ApptOrder> source,
            params BizApptOrder.OrderStatus[] orderStatuses)
        {
            Expression<Func<ApptOrder, bool>> expression = x => true;

            foreach (var orderStatus in orderStatuses)
            {
                var intOrderStatus = (int)orderStatus;
                Expression<Func<ApptOrder, bool>> exp = x => x.OrderStatus == intOrderStatus;
                var body = Expression.OrElse(expression.Body, exp.Body);
                expression = Expression.Lambda<Func<ApptOrder, bool>>(body, expression.Parameters[0]);
            }

            return source.Where(expression);
        }
    }
}